package ecoll.server;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.jdo.PersistenceManager;

import com.google.appengine.api.blobstore.BlobInfo;
import com.google.appengine.api.blobstore.BlobInfoFactory;
import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import ecoll.client.database.DatabaseService;
import ecoll.client.database.File;
import ecoll.client.database.News;
import ecoll.client.database.ProfileData;
import ecoll.client.dictionary.Definition;
import ecoll.server.data.NewsEntity;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class DatabaseServiceImpl extends RemoteServiceServlet implements
		DatabaseService {

	public ProfileData getProfileData(String login) throws RuntimeException {
		Key userKey = KeyFactory.createKey("UserEntity", login);
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		try {
			Entity userEntity = datastore.get(userKey);
			return getProfileData(userEntity);
		} catch (EntityNotFoundException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	public ArrayList<ProfileData> findProfileData(ProfileData profileData)
			throws RuntimeException {
		ArrayList<ProfileData> profiles = new ArrayList<ProfileData>();
		if (profileData.getLogin() != null) {
			profiles.add(getProfileData(profileData.getLogin()));
			return profiles;
		}
		ArrayList<Filter> filters = new ArrayList<Filter>();
		if (profileData.getFirstName() != null)
			filters.add(new FilterPredicate("firstName", FilterOperator.EQUAL,
					profileData.getFirstName()));
		if (profileData.getLastName() != null)
			filters.add(new FilterPredicate("lastName", FilterOperator.EQUAL,
					profileData.getLastName()));
		if (profileData.getMail() != null)
			filters.add(new FilterPredicate("mail", FilterOperator.EQUAL,
					profileData.getMail()));
		if (profileData.getBirthDate() != null)
			filters.add(new FilterPredicate("birthDate", FilterOperator.EQUAL,
					profileData.getBirthDate()));

		if (filters.isEmpty())
			throw new RuntimeException("Search parameters not specified!");
		Filter filter = filters.get(0);
		for (int i = 1; i < filters.size(); ++i)
			filter = CompositeFilterOperator.and(filter, filters.get(i));

		Query query = new Query("UserEntity").setFilter(filter);

		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		PreparedQuery pq = datastore.prepare(query);

		for (Entity result : pq.asIterable()) {
			profiles.add(getProfileData(result));
		}
		return profiles;
	}

	public void updateProfileData(ProfileData profileData) {
		Key userKey = KeyFactory.createKey("UserEntity", Server.getUser());
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		try {
			Entity userEntity = datastore.get(userKey);

			userEntity.setProperty("mail", profileData.getMail());
			userEntity.setProperty("firstName", profileData.getFirstName());
			userEntity.setProperty("lastName", profileData.getLastName());
			userEntity.setProperty("photo", profileData.getPhotoBlobKey());
			userEntity.setProperty("birthDate", profileData.getBirthDate());
			userEntity.setProperty("description", profileData.getDescription());
			userEntity.setProperty("registerDate",
					profileData.getRegisterDate());
			userEntity.setProperty("lastLoginDate",
					profileData.getLastLoginDate());
			userEntity.setProperty("languageLevel",
					profileData.getLanguageLevel());

			datastore.put(userEntity);
		} catch (EntityNotFoundException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	public ArrayList<News> getNews() throws IllegalArgumentException {

		PersistenceManager pm = PMF.get().getPersistenceManager();
		javax.jdo.Query query = pm.newQuery(NewsEntity.class);
		query.setFilter("user == userParam");
		query.declareParameters("String userParam");
		query.setOrdering("date desc");
		query.setRange(0, 20);

		ArrayList<News> newsList = new ArrayList<News>();
		try {
			List<NewsEntity> newsEntityList = (List<NewsEntity>) query
					.execute(Server.getUser());
			Iterator<NewsEntity> it = newsEntityList.iterator();

			while (it.hasNext()) {
				NewsEntity e = it.next();
				News news = new News(e.getUser(), e.getText(), e.getDate());
				newsList.add(news);
			}
		} finally {
			query.closeAll();
		}
		return newsList;
	}

	public void putNews(String news) {
		NewsEntity newsEntity = new NewsEntity(Server.getUser(), news);
		PMF.get().getPersistenceManager().makePersistent(newsEntity);
	}

	public ArrayList<Definition> getDefinitions(long fileId)
			throws RuntimeException {
		return Database.getDefinitions(fileId);
	}

	public void putDefinitions(ArrayList<Definition> definitions, long fileId) {
		Database.putDefinitions(definitions, fileId);
	}

	public ArrayList<File> getFiles() {
		Key userKey = KeyFactory.createKey("UserEntity", Server.getUser());
		Query query = new Query("FileRightsEntity").setAncestor(userKey);
		query.addSort("date", SortDirection.DESCENDING);

		ArrayList<File> fileList = new ArrayList<File>();
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		PreparedQuery pq = datastore.prepare(query);

		for (Entity result : pq.asIterable()) {
			Key fileKey = (Key) result.getProperty("file");
			Entity fileEntity;
			try {
				fileEntity = datastore.get(fileKey);
			} catch (EntityNotFoundException e) {
				throw new RuntimeException(e.getMessage());
			}
			long id = fileEntity.getKey().getId();
			BlobKey blobKey = (BlobKey) fileEntity.getProperty("blobKey");
			BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
			String fileName = blobInfo.getFilename();
			long size = blobInfo.getSize();
			Date date = blobInfo.getCreation();
			String rights = (String) result.getProperty("rights");

			File file = new File(id, fileName, blobKey.getKeyString(), size,
					date, rights);
			fileList.add(file);
		}
		return fileList;
	}

	public void shareFile(long fileId, String user, String rights) {
		if (!rights.equals("r") && !rights.equals("w") && !rights.equals("o"))
			throw new IllegalArgumentException("Rights: r/w/o");
		if (Server.getUser().equals(user))
			throw new RuntimeException("Unauthorized operation");

		Key fileKey = KeyFactory.createKey("FileEntity", fileId);
		Entity fileRights = Database.getRights(fileKey, Server.getUser());
		if (fileRights == null || !fileRights.getProperty("rights").equals("o"))
			throw new RuntimeException("Unauthorized operation");

		fileRights = Database.getRights(fileKey, user);
		if (fileRights == null) {
			Key userKey = KeyFactory.createKey("UserEntity", user);
			fileRights = new Entity("FileRightsEntity", userKey);
			fileRights.setProperty("file", fileKey);
			fileRights.setProperty("date", new Date());
		}
		fileRights.setProperty("rights", rights);
		DatastoreService datastore = DatastoreServiceFactory
				.getDatastoreService();
		datastore.put(fileRights);
	}

	private String escapeHtml(String html) {
		if (html == null) {
			return null;
		}
		return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
				.replaceAll(">", "&gt;");
	}

	@Override
	public String getBlobUrl(String directory) {
		BlobstoreService blobstoreService = BlobstoreServiceFactory
				.getBlobstoreService();
		String url = blobstoreService.createUploadUrl(directory);
		return url;
	}

	private ProfileData getProfileData(Entity userEntity) {
		String login = userEntity.getKey().getName();
		String mail = (String) userEntity.getProperty("mail");
		String firstName = (String) userEntity.getProperty("firstName");
		String lastName = (String) userEntity.getProperty("lastName");
		BlobKey photoBlobKey = (BlobKey) userEntity.getProperty("photo");
		String photoBlobKeyString = (photoBlobKey == null) ? null
				: photoBlobKey.getKeyString();
		Date birthDate = (Date) userEntity.getProperty("birthDate");
		String description = (String) userEntity.getProperty("description");
		Date registerDate = (Date) userEntity.getProperty("registerDate");
		Date lastLoginDate = (Date) userEntity.getProperty("lastLoginDate");
		long languageLevel = (Long) userEntity.getProperty("languageLevel");

		ProfileData data = new ProfileData(login, mail, firstName, lastName,
				photoBlobKeyString, birthDate, description, registerDate,
				lastLoginDate, languageLevel);
		return data;
	}

}
